<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js">var Element = require(&quot;./Element&quot;);

var dataUtil = require(&quot;../core/utils/data_structure_util&quot;);

var classUtil = require(&quot;../core/utils/class_util&quot;);

var PathProxy = require(&quot;./PathProxy&quot;);

var pathContain = require(&quot;../core/contain/path&quot;);

var Pattern = require(&quot;./Pattern&quot;);

var _constants = require(&quot;../graphic/constants&quot;);

var mathMax = _constants.mathMax;
var mathAbs = _constants.mathAbs;
var mathSqrt = _constants.mathSqrt;

function _typeof(obj) { &quot;@babel/helpers - typeof&quot;; if (typeof Symbol === &quot;function&quot; &amp;&amp; typeof Symbol.iterator === &quot;symbol&quot;) { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj &amp;&amp; typeof Symbol === &quot;function&quot; &amp;&amp; obj.constructor === Symbol &amp;&amp; obj !== Symbol.prototype ? &quot;symbol&quot; : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(&quot;Cannot call a class as a function&quot;); } }

function _defineProperties(target, props) { for (var i = 0; i &lt; props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (&quot;value&quot; in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _possibleConstructorReturn(self, call) { if (call &amp;&amp; (_typeof(call) === &quot;object&quot; || typeof call === &quot;function&quot;)) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(&quot;this hasn&#39;t been initialised - super() hasn&#39;t been called&quot;); } return self; }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _inherits(subClass, superClass) { if (typeof superClass !== &quot;function&quot; &amp;&amp; superClass !== null) { throw new TypeError(&quot;Super expression must either be null or a function&quot;); } subClass.prototype = Object.create(superClass &amp;&amp; superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

<span id='qrenderer-graphic-Path'>/**
</span> * @class qrenderer.graphic.Path 
 * @docauthor 大漠穷秋 &lt;damoqiongqiu@126.com&gt;
 */
var Path =
/*#__PURE__*/
function (_Element) {
  _inherits(Path, _Element);

<span id='qrenderer-graphic-Path-method-constructor'>  /**
</span>   * @method constructor Path
   * @param {Object} options
   */
  function Path(options) {
    var _this;

    _classCallCheck(this, Path);

    _this = _possibleConstructorReturn(this, _getPrototypeOf(Path).call(this, options));
<span id='qrenderer-graphic-Path-property-type'>    /**
</span>     * @property {String} type
     */

    _this.type = &#39;path&#39;;
<span id='qrenderer-graphic-Path-property-path'>    /**
</span>     * @property {PathProxy}
     * @readOnly
     */

    _this.path = null;
<span id='qrenderer-graphic-Path-property-__dirtyPath'>    /**
</span>     * @private
     * @property __dirtyPath
     */

    _this.__dirtyPath = true;
<span id='qrenderer-graphic-Path-property-strokeContainThreshold'>    /**
</span>     * @property {Number} strokeContainThreshold
     */

    _this.strokeContainThreshold = 5;
<span id='qrenderer-graphic-Path-property-segmentIgnoreThreshold'>    /**
</span>     * @property {Number} segmentIgnoreThreshold
     * This item default to be false. But in map series in echarts,
     * in order to improve performance, it should be set to true,
     * so the shorty segment won&#39;t draw.
     */

    _this.segmentIgnoreThreshold = 0;
<span id='qrenderer-graphic-Path-property-subPixelOptimize'>    /**
</span>     * @property {Boolean} subPixelOptimize
     * See `subPixelOptimize`.
     */

    _this.subPixelOptimize = false;
    return _this;
  }
<span id='qrenderer-graphic-Path-method-render'>  /**
</span>   * @method render
   * @param {Object} ctx 
   * @param {Element} prevEl 
   */


  _createClass(Path, [{
    key: &quot;render&quot;,
    value: function render(ctx, prevEl) {
      var path = this.path || new PathProxy(true);
      var hasStroke = this.style.hasStroke();
      var hasFill = this.style.hasFill();
      var fill = this.style.fill;
      var stroke = this.style.stroke;
      var hasFillGradient = hasFill &amp;&amp; !!fill.colorStops;
      var hasStrokeGradient = hasStroke &amp;&amp; !!stroke.colorStops;
      var hasFillPattern = hasFill &amp;&amp; !!fill.image;
      var hasStrokePattern = hasStroke &amp;&amp; !!stroke.image;
      this.style.bind(ctx, this, prevEl);
      this.applyTransform(ctx);

      if (this.__dirty) {
        var rect; // Update gradient because bounding rect may changed

        if (hasFillGradient) {
          rect = rect || this.getBoundingRect();
          this._fillGradient = this.style.getGradient(ctx, fill, rect);
        }

        if (hasStrokeGradient) {
          rect = rect || this.getBoundingRect();
          this._strokeGradient = this.style.getGradient(ctx, stroke, rect);
        }
      } // Use the gradient or pattern


      if (hasFillGradient) {
        // PENDING If may have affect the state
        ctx.fillStyle = this._fillGradient;
      } else if (hasFillPattern) {
        ctx.fillStyle = Pattern.prototype.getCanvasPattern.call(fill, ctx);
      }

      if (hasStrokeGradient) {
        ctx.strokeStyle = this._strokeGradient;
      } else if (hasStrokePattern) {
        ctx.strokeStyle = Pattern.prototype.getCanvasPattern.call(stroke, ctx);
      }

      var lineDash = this.style.lineDash;
      var lineDashOffset = this.style.lineDashOffset;
      var ctxLineDash = !!ctx.setLineDash; // Update path sx, sy

      var scale = this.getGlobalScale();
      path.setScale(scale[0], scale[1], this.segmentIgnoreThreshold); // Proxy context
      // Rebuild path in following 2 cases
      // 1. Path is dirty
      // 2. Path needs javascript implemented lineDash stroking.
      //    In this case, lineDash information will not be saved in PathProxy

      if (this.__dirtyPath || lineDash &amp;&amp; !ctxLineDash &amp;&amp; hasStroke) {
        path.beginPath(ctx); // Setting line dash before build path

        if (lineDash &amp;&amp; !ctxLineDash) {
          path.setLineDash(lineDash);
          path.setLineDashOffset(lineDashOffset);
        }

        this.buildPath(path, this.shape, false); // Clear path dirty flag

        if (this.path) {
          this.__dirtyPath = false;
        }
      } else {
        // Replay path building
        ctx.beginPath();
        this.path.rebuildPath(ctx);
      }

      if (hasFill) {
        if (this.style.fillOpacity != null) {
          var originalGlobalAlpha = ctx.globalAlpha;
          ctx.globalAlpha = this.style.fillOpacity * this.style.opacity;
          path.fill(ctx);
          ctx.globalAlpha = originalGlobalAlpha;
        } else {
          path.fill(ctx);
        }
      }

      if (lineDash &amp;&amp; ctxLineDash) {
        ctx.setLineDash(lineDash);
        ctx.lineDashOffset = lineDashOffset;
      }

      if (hasStroke) {
        if (this.style.strokeOpacity != null) {
          var _originalGlobalAlpha = ctx.globalAlpha;
          ctx.globalAlpha = this.style.strokeOpacity * this.style.opacity;
          path.stroke(ctx);
          ctx.globalAlpha = _originalGlobalAlpha;
        } else {
          path.stroke(ctx);
        }
      }

      if (lineDash &amp;&amp; ctxLineDash) {
        // PENDING
        // Remove lineDash
        ctx.setLineDash([]);
      }

      Element.prototype.render.call(this, ctx, prevEl); // Draw rect text

      if (this.style.text != null) {
        // Only restore transform when needs draw text.
        this.restoreTransform(ctx);
        this.drawRectText(ctx, this.getBoundingRect());
      }
    }
<span id='qrenderer-graphic-Path-method-buildPath'>    /**
</span>     * @method buildPath
     * 
     * Each subclass should provide its own implement for this method.
     * When build path, some shape may decide if use moveTo to begin a new subpath or closePath, like in circle.
     * 
     * 每个子类都需要为此方法提供自己的实现。
     * 在构建路径时，某些形状需要根据情况决定使用 moveTo 来开始一段子路径，或者直接用 closePath 来封闭路径，比如圆形。
     * 
     * @param {*} ctx 
     * @param {*} shapeCfg 
     * @param {*} inBundle 
     */

  }, {
    key: &quot;buildPath&quot;,
    value: function buildPath(ctx, shapeCfg, inBundle) {}
<span id='qrenderer-graphic-Path-method-createPathProxy'>    /**
</span>     * @method createPathProxy
     */

  }, {
    key: &quot;createPathProxy&quot;,
    value: function createPathProxy() {
      this.path = new PathProxy();
    }
<span id='qrenderer-graphic-Path-method-getBoundingRect'>    /**
</span>     * @method getBoundingRect
     * Get bounding rect of this element.
     * NOTE: this method will return the bounding rect without transforming.
     * 
     * 
     * 获取当前元素的边界矩形。
     * 注意：此方法返回的是没有经过 transform 处理的边界矩形。
     */

  }, {
    key: &quot;getBoundingRect&quot;,
    value: function getBoundingRect() {
      var rect = this._boundingRect;
      var needsUpdateRect = !rect;

      if (needsUpdateRect) {
        var path = this.path;

        if (!path) {
          // Create path on demand.
          path = this.path = new PathProxy();
        }

        if (this.__dirtyPath) {
          path.beginPath();
          this.buildPath(path, this.shape, false);
        }

        rect = path.getBoundingRect();
      }

      this._boundingRect = rect;

      if (this.style.hasStroke()) {
        // Update rect with stroke lineWidth when
        // 1. Element changes scale or lineWidth
        // 2. Shape is changed
        var rectWithStroke = this._boundingRectWithStroke || (this._boundingRectWithStroke = rect.clone());

        if (this.__dirty || needsUpdateRect) {
          rectWithStroke.copy(rect); // FIXME Must after composeLocalTransform

          var w = this.style.lineWidth; // PENDING, Min line width is needed when line is horizontal or vertical

          var lineScale = this.style.strokeNoScale ? this.getLineScale() : 1; // Only add extra hover lineWidth when there are no fill

          if (!this.style.hasFill()) {
            w = mathMax(w, this.strokeContainThreshold || 4);
          } // Consider line width
          // Line scale can&#39;t be 0;


          if (lineScale &gt; 1e-10) {
            rectWithStroke.width += w / lineScale;
            rectWithStroke.height += w / lineScale;
            rectWithStroke.x -= w / lineScale / 2;
            rectWithStroke.y -= w / lineScale / 2;
          }
        } // Return rect with stroke


        return rectWithStroke;
      }

      return rect;
    }
<span id='qrenderer-graphic-Path-method-contain'>    /**
</span>     * @method contain
     * @param {*} x 
     * @param {*} y 
     */

  }, {
    key: &quot;contain&quot;,
    value: function contain(x, y) {
      var localPos = this.globalToLocal(x, y);
      var rect = this.getBoundingRect();
      var style = this.style;
      x = localPos[0];
      y = localPos[1];

      if (rect.contain(x, y)) {
        var pathData = this.path.data;

        if (style.hasStroke()) {
          var lineWidth = style.lineWidth;
          var lineScale = style.strokeNoScale ? this.getLineScale() : 1; // Line scale can&#39;t be 0;

          if (lineScale &gt; 1e-10) {
            // Only add extra hover lineWidth when there are no fill
            if (!style.hasFill()) {
              lineWidth = mathMax(lineWidth, this.strokeContainThreshold);
            }

            if (pathContain.containStroke(pathData, lineWidth / lineScale, x, y)) {
              return true;
            }
          }
        }

        if (style.hasFill()) {
          return pathContain.contain(pathData, x, y);
        }
      }

      return false;
    }
<span id='qrenderer-graphic-Path-method-dirty'>    /**
</span>     * @protected
     * @method dirty
     * @param  {Boolean} dirtyPath
     */

  }, {
    key: &quot;dirty&quot;,
    value: function dirty(dirtyPath) {
      if (dirtyPath == null) {
        dirtyPath = true;
      } // Only mark dirty, not mark clean


      if (dirtyPath) {
        this.__dirtyPath = dirtyPath;
      }

      Element.prototype.dirty.call(this); // Used as a clipping path

      if (this.__clipTarget) {
        this.__clipTarget.dirty();
      }
    }
<span id='qrenderer-graphic-Path-method-_attrKV'>    /**
</span>     * @method _attrKV
     * Overwrite _attrKV
     * @param {*} key 
     * @param {Object} value 
     */

  }, {
    key: &quot;_attrKV&quot;,
    value: function _attrKV(key, value) {
      // FIXME
      if (key === &#39;shape&#39;) {
        this.__dirtyPath = true;
        this._boundingRect = null;
        this.setShape(value);
      } else {
        Element.prototype._attrKV.call(this, key, value);
      }
    }
<span id='qrenderer-graphic-Path-method-setShape'>    /**
</span>     * @method setShape
     * @param {Object|String} key
     * @param {Object} value
     */

  }, {
    key: &quot;setShape&quot;,
    value: function setShape(key, value) {
      // Path from string may not have shape
      if (!this.shape) {
        return this;
      }

      if (dataUtil.isObject(key)) {
        classUtil.copyOwnProperties(this.shape, key);
      } else {
        this.shape[key] = value;
      }

      this.dirty(true);
      return this;
    }
<span id='qrenderer-graphic-Path-method-getLineScale'>    /**
</span>     * @method getLineScale
     */

  }, {
    key: &quot;getLineScale&quot;,
    value: function getLineScale() {
      var m = this.transform; // Get the line scale.
      // Determinant of `m` means how much the area is enlarged by the
      // transformation. So its square root can be used as a scale factor
      // for width.

      return m &amp;&amp; mathAbs(m[0] - 1) &gt; 1e-10 &amp;&amp; mathAbs(m[3] - 1) &gt; 1e-10 ? mathSqrt(mathAbs(m[0] * m[3] - m[2] * m[1])) : 1;
    }
  }]);

  return Path;
}(Element);

var _default = Path;
module.exports = _default;</pre>
</body>
</html>
